﻿using System.Collections.Generic;
using System.IO;

namespace Wizard.Compiler.Tokenizers
{
    internal class SourceStack
    {

        private readonly Stack<SourcePos> mSourcePosStack;
        private SourcePos mCurrentPos;
        private BaseTokenizer mCurrentTokenizer;

        internal SourceStack()
        {
            mSourcePosStack = new Stack<SourcePos>();
        }

        public string Name
        {
            get { return mCurrentPos.Name; }
        }

        public int Line
        {
            get { return mCurrentPos.Line; }
        }

        public int Column
        {
            get { return mCurrentPos.Column; }
        }

        public void PushExprString(string name, string source, Language language)
        {
            INamedTextReader textReader = new StringTextReader(name, source);
            if (mCurrentPos != null) mSourcePosStack.Push(mCurrentPos);
            mCurrentPos = new SourcePos(this, textReader, language);
        }

        public void PushFileSource(string filename)
        {
            INamedTextReader textReader = new FileTextReader(filename);
            if (mCurrentPos != null) mSourcePosStack.Push(mCurrentPos);
            Language language;
            string ext = Path.GetExtension(filename).ToLower();
            switch (ext)
            {
                case ".bas":
                case ".vb":
                    language = Language.Basic;
                    break;
                case ".c":
                case ".cs":
                case ".cpp":
                case ".java":
                    language = Language.Java;
                    break;
                case ".pas":
                    language = Language.Pascal;
                    break;
                default:
                    throw new Engine.WizardException(string.Format("Unknown file extension {0}.", ext));
            }
            mCurrentPos = new SourcePos(this, textReader, language);
        }

        public int ReadChar()
        {
            var result = mCurrentPos.ReadChar();
            while (result == -1 && mSourcePosStack.Count > 0)
            {
                mCurrentPos = mSourcePosStack.Pop();
                result = mCurrentPos.ReadChar();
            }
            return result;
        }

        #region Nested type: FileSource

        private class FileTextReader : INamedTextReader
        {
            private readonly string mFilename;
            private readonly StreamReader mSr;

            public FileTextReader(string filename)
            {
                mFilename = filename;
                mSr = new StreamReader(mFilename);
            }

            #region ISource Members

            string INamedTextReader.Name
            {
                get { return mFilename; }
            }

            int INamedTextReader.ReadChar()
            {
                return mSr.Read();
            }

            #endregion
        }

        #endregion

        #region Nested type: StringSource

        private class StringTextReader : INamedTextReader
        {
            private readonly string mName;
            private readonly string mSource;
            private readonly StringReader mSourceReader;

            public StringTextReader(string name, string source)
            {
                mName = name;
                mSource = source;
                mSourceReader = new StringReader(mSource);
            }

            #region ISource Members

            string INamedTextReader.Name
            {
                get { return mName; }
            }

            int INamedTextReader.ReadChar()
            {
                return mSourceReader.Read();
            }

            #endregion
        }

        #endregion

        internal BaseTokenizer CurrentTokenizer
        {
            get { return mCurrentPos.Tokenizer; }

        }
    }
}